﻿using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using NekoMeko.Common;
using NekoMeko.Components;
using Neorxna;
using Neorxna.Common;
using Neorxna.Graphic;
using System;
using System.Collections.Generic;

namespace NekoMeko.Managers
{
	public class NekoMekoModLoader : ModLoader
	{
		public override void __ModInitialize()
		{
			ModsManager.RegisterHook("GetMaxInstancesCount", this);
			ModsManager.RegisterHook("OnLoadingStart", this);
			ModsManager.RegisterHook("OnProjectLoaded", this);
			ModsManager.RegisterHook("BeforeWidgetUpdate", this);
			ModsManager.RegisterHook("AfterWidgetUpdate", this);
			ModsManager.RegisterHook("OnPlayerModelWidgetMeasureOverride", this);
			ModsManager.RegisterHook("OnScreenLeaved", this);
			ModsManager.RegisterHook("OnFirstPersonModelDrawing", this);
			ModsManager.RegisterHook("GetCameraList", this);
			ModsManager.RegisterHook("ManageCameras", this);
		}
		public override int GetMaxInstancesCount() => 32;
		public override void OnLoadingStart(List<Action> actions)
		{
			NekoMekoDataManager.Initialize();
		}
		public override void OnProjectLoaded(Project project)
		{
			SubsystemPlayers subsystemPlayers = project.FindSubsystem<SubsystemPlayers>();
			if (subsystemPlayers != null)
			{
				subsystemPlayers.PlayerAdded += (playerData) =>
				{//当玩家数据成功添加但没有待添加模型皮肤数据，说明没有点进切换皮肤界面，需要手动补上初始化数据
					bool isMale = playerData.PlayerClass == PlayerClass.Male;
					if (string.IsNullOrEmpty(NekoMekoDataManager.CreatingPlayerModelKey))
					{
						string initialModelKey = isMale ? NekoMekoDataManager.DefaultMaleModelKey : NekoMekoDataManager.DefaultFemaleModelKey;
						NekoMekoDataManager.CreatingPlayerModelKey = initialModelKey;
					}
					if (string.IsNullOrEmpty(NekoMekoDataManager.CreatingPlayerSkinKey))
					{
						string initialSkinKey = NekoMekoDataManager.GetSkinKeyByCharacterSkinName(playerData.CharacterSkinName);
						if (string.IsNullOrEmpty(initialSkinKey))
							initialSkinKey = isMale ? NekoMekoDataManager.DefaultMaleSkinKey : NekoMekoDataManager.DefaultFemaleSkinKey;
						NekoMekoDataManager.CreatingPlayerSkinKey = initialSkinKey;
					}
				};
			}
		}
		public override void BeforeWidgetUpdate(Widget widget)
		{
			if (widget is PlayerScreen playerScreen)
			{
				if (playerScreen.m_characterSkinButton.IsClicked)
				{
					if (playerScreen.m_mode == PlayerScreen.Mode.Edit)
					{//编辑现有玩家数据
						ComponentNekoMekoModel componentNekoMekoModel = playerScreen.m_playerData?.ComponentPlayer?.Entity.FindComponent<ComponentNekoMekoModel>();
						if (componentNekoMekoModel != null)
						{
							Dialog dialog = new NekoMekoCharacterDialog(playerScreen.m_playerData, (selectedModelKey, selectedSkinKey) =>
							{
								componentNekoMekoModel.SetResModel(selectedModelKey);
								componentNekoMekoModel.SetResSkin(selectedSkinKey);
								NekoMekoDataManager.CreatingPlayerModelKey = selectedModelKey;
								NekoMekoDataManager.CreatingPlayerSkinKey = selectedSkinKey;
								NekoMekoDataManager.EditingPlayerIndex = playerScreen.m_playerData.PlayerIndex;
							}, componentNekoMekoModel.ModelKey, componentNekoMekoModel.SkinKey);
							DialogsManager.ShowDialog(null, dialog);
						}
					}
					else
					{//初始化或创建新玩家
						bool isMale = playerScreen.m_playerData.PlayerClass == PlayerClass.Male;
						string initialModelKey = isMale ? NekoMekoDataManager.DefaultMaleModelKey : NekoMekoDataManager.DefaultFemaleModelKey;
						string initialSkinKey = NekoMekoDataManager.GetSkinKeyByCharacterSkinName(playerScreen.m_playerData.CharacterSkinName);
						if(string.IsNullOrEmpty(initialSkinKey))
							initialSkinKey = isMale ? NekoMekoDataManager.DefaultMaleSkinKey : NekoMekoDataManager.DefaultFemaleSkinKey;
						Dialog dialog = new NekoMekoCharacterDialog(playerScreen.m_playerData, (selectedModelKey, selectedSkinKey) =>
						{
							NekoMekoDataManager.CreatingPlayerModelKey = selectedModelKey;
							NekoMekoDataManager.CreatingPlayerSkinKey = selectedSkinKey;
							NekoMekoDataManager.EditingPlayerIndex = playerScreen.m_playerData.PlayerIndex;
						}, initialModelKey, initialSkinKey);
						DialogsManager.ShowDialog(null, dialog);
					}
				}
				if (playerScreen.m_characterSkinButton is BevelledButtonWidget bevelledButtonWidget)
					bevelledButtonWidget.m_clickableWidget.IsClicked = false;
			}
		}
		public override void AfterWidgetUpdate(Widget widget)
		{
			if (widget is PlayerScreen playerScreen)
			{
				ComponentNekoMekoModel componentNekoMekoModel = playerScreen.m_playerData?.ComponentPlayer?.Entity.FindComponent<ComponentNekoMekoModel>();
				if (componentNekoMekoModel != null && componentNekoMekoModel.ResSkin != null)
				{
					playerScreen.m_characterSkinLabel.Text = componentNekoMekoModel.ResSkin.DisplayName;
				}
			}
		}
		public override void OnPlayerModelWidgetMeasureOverride(PlayerModelWidget playerModelWidget)
		{
			bool flag = false;
			NekoResModel nekoResModel = null;
			NekoResSkin nekoResSkin = null;
			Texture2D clothedTexture = null, outerClothedTexture = null;
			if (playerModelWidget.ParentWidget != null && playerModelWidget.ParentWidget is StackPanelWidget)
			{//列表中玩家模型调整角度
				playerModelWidget.m_modelWidget.ViewPosition = new Vector3(-2.5f, 2.5f, -2.5f);
			}
			if (playerModelWidget.ExtraData != null && playerModelWidget.ExtraData is ExtraData extraData && extraData.Type == "NMM-PlayerModelWidget")
			{
				flag = true;
				if (extraData.Data is string[] objs && objs.Length == 2)
				{
					nekoResModel = NekoMekoDataManager.FindModelData(objs[0]);
					nekoResSkin = NekoMekoDataManager.FindSkinData(objs[1]);
				}
			}
			else if (playerModelWidget.PlayerData != null)
			{
				if (playerModelWidget.PlayerData.PlayerIndex == NekoMekoDataManager.EditingPlayerIndex)
				{
					flag = true;
					nekoResModel = NekoMekoDataManager.FindModelData(NekoMekoDataManager.CreatingPlayerModelKey);
					nekoResSkin = NekoMekoDataManager.FindSkinData(NekoMekoDataManager.CreatingPlayerSkinKey);
				}
				else
				{
					ComponentNekoMekoModel componentNekoMekoModel = playerModelWidget.PlayerData.ComponentPlayer?.Entity.FindComponent<ComponentNekoMekoModel>();
					if (componentNekoMekoModel != null)
					{
						flag = true;
						nekoResModel = componentNekoMekoModel.ResModel;
						nekoResSkin = componentNekoMekoModel.ResSkin;
						if (nekoResModel?.AllowClotheOverride ?? false)
						{
							clothedTexture = componentNekoMekoModel.Texture;
							if (componentNekoMekoModel.ComponentNeoModel?.ComponentClothing != null)
								outerClothedTexture = componentNekoMekoModel.ComponentNeoModel.ComponentClothing.OuterClothedTexture;
						}
					}
				}
			}
			if (flag)
			{
				playerModelWidget.m_modelWidget.Models.Clear();
				if (nekoResModel != null && (nekoResModel.ResPath?.TryGetValue("ModelName", out var modelName) ?? false) && !string.IsNullOrEmpty(modelName))
				{//分配模型
					Model model = nekoResModel.GetRes(modelName) as Model;
					playerModelWidget.m_modelWidget.AddModel(model);
					if (nekoResSkin != null && (nekoResSkin.ResPath?.TryGetValue("TextureName", out var textureName) ?? false) && !string.IsNullOrEmpty(textureName))
					{//分配皮肤贴图和区分着色器
						Texture2D texture = clothedTexture ?? nekoResSkin.GetRes(textureName) as Texture2D;
						playerModelWidget.m_modelWidget.Textures[model] = texture;
						switch (nekoResSkin.RenderMode)
						{
							case ObjectRenderMode.Unlit:
							{
								playerModelWidget.m_modelWidget.CustomShader = NeorxnaRenderer.UnlitShader;
								playerModelWidget.m_modelWidget.OnSetupShaderParameters = (modelWidget, shader, model, mesh) =>
								{
									if (shader is not UnlitShader unlitShader)
										return;
									var color = new Vector4(Color.White);
									unlitShader.SamplerState = SamplerState.PointClamp;
									unlitShader.Color = color;
									unlitShader.AlphaThreshold = 0f;
								};
								break;
							}
							case ObjectRenderMode.PBR:
							{
								playerModelWidget.m_modelWidget.CustomShader = NeorxnaRenderer.PBRShader;
								playerModelWidget.m_modelWidget.OnSetupShaderParameters = (modelWidget, shader, model, mesh) =>
								{
									if (shader is not PBRShader pbrShader)
										return;
									var color = new Vector3(Color.White);
									var ambientLight = 1f;
									if ((nekoResSkin.ResPath?.TryGetValue("PBRMapName", out var pbrMapName) ?? false) && !string.IsNullOrEmpty(pbrMapName))
										pbrShader.PBRMap = nekoResSkin.GetRes(pbrMapName) as Texture2D;
									else
										pbrShader.PBRMap = null;
									if ((nekoResSkin.ResPath?.TryGetValue("NormalMapName", out var normalMapName) ?? false) && !string.IsNullOrEmpty(normalMapName))
										pbrShader.NormalMap = nekoResSkin.GetRes(normalMapName) as Texture2D;
									else
										pbrShader.NormalMap = null;
									pbrShader.SkyBox = NeorxnaRenderer.DefaultSkyBox;
									pbrShader.SamplerState = SamplerState.PointClamp;
									pbrShader.MapsSamplerState = SamplerState.PointClamp;
									pbrShader.AmbientLightColor = new Vector3(ambientLight * Math.Clamp(LightingManager.LightAmbient + 0.4f, 0.05f, 1f));
									pbrShader.DiffuseLightColor1 = color;
									pbrShader.DiffuseLightColor2 = color;
									pbrShader.DiffuseLightColor3 = color;
									pbrShader.DirectionToLight1 = LightingManager.DirectionToLight1;
									pbrShader.DirectionToLight2 = LightingManager.DirectionToLight2;
									pbrShader.DirectionToLight3 = new(0.7f, 0.25f, -0.34f);
									pbrShader.LightIntensity = new Vector3(1f, 1f, 1f) * LightingManager.LightAmbient;
									pbrShader.CameraPosition = modelWidget.ViewPosition;
									pbrShader.AlphaThreshold = 0f;
									pbrShader.Wetness = 0f;
								};
								break;
							}
							default:
							{
								playerModelWidget.m_modelWidget.CustomShader = null;
								playerModelWidget.m_modelWidget.OnSetupShaderParameters = null;
								break;
							}
						}
					}
					if (outerClothedTexture != null && (nekoResModel.ResPath?.TryGetValue("OuterClothingModelName", out var outerModelName) ?? false) && !string.IsNullOrEmpty(outerModelName))
					{
						Model model2 = nekoResModel.GetRes(outerModelName) as Model;
						playerModelWidget.m_modelWidget.AddModel(model2);
						playerModelWidget.m_modelWidget.Textures[model2] = outerClothedTexture;
					}
				}
			}
		}

		public override void OnScreenLeaved(Screen screen)
		{
			if (screen is PlayerScreen playerScreen)
			{
				playerScreen.m_playerModel.ExtraData = null;
				playerScreen.m_playerModel.m_modelWidget.Models.Clear();
				playerScreen.m_playerModel.m_modelWidget.Textures.Clear();
			}
		}

		public override void OnFirstPersonModelDrawing(ComponentFirstPersonModel componentFirstPersonModel, Camera camera, int itemValue, ref Matrix matrix, out bool skip)
		{
			skip = false;
			if (Terrain.ExtractContents(itemValue) <= 0)
			{//空手
				ComponentNekoMekoModel componentNekoMekoModel = componentFirstPersonModel.Entity.FindComponent<ComponentNekoMekoModel>();
				if (componentNekoMekoModel != null && componentNekoMekoModel.FirstPersonModel != null)
				{
					Vector3 eyePosition = componentFirstPersonModel.m_componentPlayer.ComponentCreatureModel.EyePosition;
					Matrix m = Matrix.CreateFromQuaternion(componentFirstPersonModel.m_componentPlayer.ComponentCreatureModel.EyeRotation);
					m.Translation = eyePosition;
					var position4 = new Vector3(0.25f, -0.3f, -0.05f) + new Vector3(-0.2f, 0.06f, -0.05f);
					Matrix transform = Matrix.CreateRotationX(0.8f) * Matrix.CreateRotationY(0.4f) * matrix * Matrix.CreateTranslation(position4) * Matrix.CreateFromYawPitchRoll(componentFirstPersonModel.m_lagAngles.X, componentFirstPersonModel.m_lagAngles.Y, 0f) * m;
					float ambientLight = componentFirstPersonModel.m_handLight;//NeorxnaRenderer.GetAmbientLight(eyePosition);
					foreach (var mesh in componentNekoMekoModel.FirstPersonModel.Meshes)
					{
						Matrix world = BlockMesh.GetBoneAbsoluteTransform(mesh.ParentBone) * transform;
						NeorxnaRenderer.DrawLitObject(camera, world, ambientLight, mesh, componentNekoMekoModel.Texture);
					}
					skip = true;
				}
			}
		}

		public override IEnumerable<KeyValuePair<string, int>> GetCameraList()
		{
			yield return new("NekoMeko.Common.ShoulderSurfingCamera", 5);
		}
		public override void ManageCameras(GameWidget gameWidget)
		{
			gameWidget.AddCamera(new ShoulderSurfingCamera(gameWidget));
		}
	}
}
